//----------------------------------------------------------------
//module name : yhz_memory
//engineer : yhz
//date : 2021.07.28
//----------------------------------------------------------------
`include "yhz_defines.v"
module yhz_memory (
    input  wire        i_clk             ,
    input  wire        i_rst             ,
    input  wire        i_pipeline_pulse  ,
    output wire        o_pipeline_unlock ,
    output wire        o_pipeline_pulse  ,
    //command
    input  wire        i_load_en         ,
    input  wire        i_store_en        ,
    input  wire [4:0]  i_load_cmd        ,
    input  wire [63:0] i_store_data      ,
    input  wire [63:0] i_store_mask      ,
    //transfer
    input  wire        i_w_rd_en         ,
    input  wire [4:0]  i_w_rd_addr       ,
    input  wire [63:0] i_w_rd_data       ,
    output wire [63:0] o_w_rd_data_t     ,
    //to_write_back
    output wire        o_w_rd_en         ,
    output wire [4:0]  o_w_rd_addr       ,
    output wire [63:0] o_w_rd_data       ,
    //clint
    output wire        o_w_mtime_en      ,
    output wire        o_r_mtime_en      ,
    output wire        o_w_mtimecmp_en   ,
    output wire        o_r_mtimecmp_en   ,
    input  wire [63:0] i_r_clint_data    ,
    //to_AXI
    input  wire        i_w_ram_ready     ,
    output wire        o_w_ram_valid     ,
    output wire [63:0] o_w_ram_addr      ,
    output wire [63:0] o_w_ram_data      ,
    output wire [63:0] o_w_ram_mask      ,
    input  wire        i_r_ram_ready     ,
    output wire        o_r_ram_valid     ,
    output wire [63:0] o_r_ram_addr      ,
    input  wire [63:0] i_r_ram_data       
);
//----------------------------------------------------------------
//register & wire
//----------------------------------------------------------------
    reg         pipeline_pulse ;
    //rd
    wire        w_rd_en_t      ;
    wire [4:0]  w_rd_addr_t    ;
    reg  [63:0] w_rd_data_t    ;
    reg         w_rd_en        ;
    reg  [4:0]  w_rd_addr      ;
    reg  [63:0] w_rd_data      ;
    //clint
    wire        w_mtime_en        = i_store_en && (w_ram_addr == `MTIME_ADDR   ) ;
    wire        r_mtime_en        = i_load_en  && (r_ram_addr == `MTIME_ADDR   ) ;
    wire        w_mtimecmp_en     = i_store_en && (w_ram_addr == `MTIMECMP_ADDR) ;
    wire        r_mtimecmp_en     = i_load_en  && (r_ram_addr == `MTIMECMP_ADDR) ;
    //ram
    wire        w_ram_valid       = i_store_en ? (w_mtime_en | w_mtimecmp_en) ? 1'b0 : 1'b1 : 1'b0 ;
    wire        r_ram_valid       = i_load_en  ? (r_mtime_en | r_mtimecmp_en) ? 1'b0 : 1'b1 : 1'b0 ;
    wire [63:0] w_ram_addr        = i_store_en ? i_w_rd_data : 64'd0 ;
    wire [63:0] r_ram_addr        = i_load_en  ? i_w_rd_data : 64'd0 ;
    wire [63:0] w_ram_data        = i_store_en ? (i_store_data << store_shift) : 64'd0 ;
    wire [63:0] w_ram_mask        = i_store_en ? (i_store_mask << store_shift) : 64'd0 ;

    wire [5:0]  store_shift       = w_ram_addr[2:0] << 3 ;
    wire [5:0]  load_shift        = r_ram_addr[2:0] << 3 ;
    wire [63:0] r_ram_data        = (r_mtime_en | r_mtimecmp_en) ? (i_r_clint_data >> load_shift) : (i_r_ram_data >> load_shift) ;
    wire        hand_shake_flag_w = (w_mtime_en | w_mtimecmp_en) ? 1'b1 : (w_ram_valid & i_w_ram_ready) ;
    wire        hand_shake_flag_r = (r_mtime_en | r_mtimecmp_en) ? 1'b1 : (r_ram_valid & i_r_ram_ready) ;
    wire        pipeline_unlock   = hand_shake_flag_w | hand_shake_flag_r ;
//----------------------------------------------------------------
//logic
//----------------------------------------------------------------
    //w_rd_data_t
    always @(*) begin
        if(i_rst) begin
            w_rd_data_t = 64'd0 ;
        end
        else if(i_load_en) begin
            case(i_load_cmd)
                5'b00011 : w_rd_data_t = {{56{r_ram_data[7]}} ,r_ram_data[7:0] } ;
                5'b00101 : w_rd_data_t = {{48{r_ram_data[15]}},r_ram_data[15:0]} ;
                5'b01001 : w_rd_data_t = {{32{r_ram_data[31]}},r_ram_data[31:0]} ;
                5'b00010 : w_rd_data_t = {56'd0,r_ram_data[7:0] } ;
                5'b00100 : w_rd_data_t = {48'd0,r_ram_data[15:0]} ;
                5'b01000 : w_rd_data_t = {32'd0,r_ram_data[31:0]} ;
                5'b10001 : w_rd_data_t = r_ram_data ;
                default  : w_rd_data_t = 64'd0 ;
            endcase
        end
        else begin
            w_rd_data_t = i_w_rd_data ;
        end
    end
    //pipeline_pulse
    always @(posedge i_clk) begin
        if(i_rst) begin
            pipeline_pulse <= 1'b0 ;
        end
        else if(pipeline_unlock) begin
            pipeline_pulse <= 1'b0 ;
        end
        else begin
            pipeline_pulse <= i_pipeline_pulse ;
        end
    end
    //w_rd_en
    always @(posedge i_clk) begin
        if(i_rst) begin
            w_rd_en <= 1'b0 ;
        end
        else if(pipeline_pulse & (!pipeline_unlock)) begin
            w_rd_en <= w_rd_en ;
        end
        else begin
            w_rd_en <= i_w_rd_en ;
        end
    end
    //w_rd_addr
    always @(posedge i_clk) begin
        if(i_rst) begin
            w_rd_addr <= 5'd0 ;
        end
        else if(pipeline_pulse & (!pipeline_unlock)) begin
            w_rd_addr <= w_rd_addr ;
        end
        else begin
            w_rd_addr <= i_w_rd_addr ;
        end
    end
    //w_rd_data
    always @(posedge i_clk) begin
        if(i_rst) begin
            w_rd_data <= 64'd0 ;
        end
        else if(pipeline_pulse & (!pipeline_unlock)) begin
            w_rd_data <= w_rd_data ;
        end
        else begin
            w_rd_data <= w_rd_data_t ;
        end
    end
//----------------------------------------------------------------
//output
//----------------------------------------------------------------
    //transfer
    assign o_pipeline_unlock = pipeline_unlock ;
    assign o_pipeline_pulse  = pipeline_pulse  ;
    assign o_w_rd_data_t     = w_rd_data_t     ;
    //rd
    assign o_w_rd_en         = w_rd_en         ;
    assign o_w_rd_addr       = w_rd_addr       ;
    assign o_w_rd_data       = w_rd_data       ;
    //clint
    assign o_w_mtime_en      = w_mtime_en      ;
    assign o_r_mtime_en      = r_mtime_en      ;
    assign o_w_mtimecmp_en   = w_mtimecmp_en   ;
    assign o_r_mtimecmp_en   = r_mtimecmp_en   ;
    //ram
    assign o_w_ram_valid     = w_ram_valid     ;
    assign o_w_ram_addr      = w_ram_addr      ;
    assign o_w_ram_data      = w_ram_data      ;
    assign o_w_ram_mask      = w_ram_mask      ;
    assign o_r_ram_valid     = r_ram_valid     ;
    assign o_r_ram_addr      = r_ram_addr      ;
//----------------------------------------------------------------
endmodule
//----------------------------------------------------------------
